/**
 * We want minimize time for calculations but we don't want affect accuracy =>
 * For each set of the parameters N, J, B, T, dT, modelName, method we want find correlation length
 */
package simulation;

import static common.PathJoin.pathJoin;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

import common.Stat;
import common.String2Array;

/**
 * @author vladimir
 * 
 */
public class CorrLength {
	int sweepStep = 0;
	int MCS = 0;
	int ThermalMCS = 0;
	int N;
	double J;
	double B;
	double T;
	double dT;
	String modelName;
	String method;
	int Correlation_Length = 0;

	public CorrLength(int N, double J, double B, double T, double dT,
			String modelName, String method) throws IOException {

		this.N = N;
		this.J = J;
		this.B = B;
		this.T = T;
		this.dT = dT;
		this.modelName = modelName;
		this.method = method;

		// println();
		// we try read data from config file
		SAXBuilder builder = new SAXBuilder();

		String path = pathJoin("src", "config", "CorrelationLength", "N_" + N,
				"J_" + J, "B_" + B, "dT_" + dT, method);

		File f = new File("./");

		String file_path = f.getCanonicalPath() + path;

		boolean success = (new File(file_path)).mkdirs();// Create
		// folder
		// File xmlFile = new File("src/config/CorrelationLength.xml");
		File xmlFile = new File(file_path + "/" + "CorrelationLength.xml");
		Document document = new Document(new Element("main"));

		try {
			document = (Document) builder.build(xmlFile);

		} catch (IOException io) {
			System.out.println(io.getMessage());
		} catch (JDOMException jdomex) {
			System.out.println(jdomex.getMessage());
		}

		Element rootNode = document.getRootElement();
		Element model = rootNode.getChild(modelName);
		// if there is nothing about this model we create Element with name as
		// our model
		if (model == null) {// if there is no such model we add it there
			List rootNodeChildren = rootNode.getChildren();
			rootNodeChildren.add(new Element(modelName));
		}
		model = rootNode.getChild(modelName);
		// System.out.println(model);

		// now we get list of all parameters
		List<Element> corLengths = model.getChildren();
		// now we scan through parameters and search if there is something
		// corresponding to our model
		for (int i = 0; i < corLengths.size(); i++) {
			Element corLength = corLengths.get(i);
			if (corLength.getAttributeValue("N").equals("" + this.N)) {// if we
																		// have
																		// something
																		// with
																		// this
																		// lattice
																		// size
				if (corLength.getAttributeValue("J").equals("" + this.J)) {// check
																			// if
																			// J
																			// matches
					if (corLength.getAttributeValue("B").equals("" + this.B)) {// if
																				// B
																				// mathches
						if (corLength.getAttributeValue("T")
								.equals("" + this.T)) {// if
														// T
														// matches
							if (corLength.getAttributeValue("dT").equals(
									"" + this.dT)) {// if dT matches
								this.Correlation_Length = Integer
										.parseInt(corLength.getText());// we get
																		// correlation
																		// length
																		// from
																		// file
							}
						}
					}
				}

			}
		}

		if (this.Correlation_Length == 0) {// if we have not found our
											// Correlation length in file we
											// should do simulation and analyze
											// results
			Double[] M2 = Simlation();// get magnetisation squared value from
										// simulation
			this.Correlation_Length = Stat.corLen(M2);
			// save data to file

			Element xml_cor = new Element("Correlation_length");
			xml_cor.setAttribute("N", "" + this.N);
			xml_cor.setAttribute("J", "" + this.J);
			xml_cor.setAttribute("B", "" + this.B);
			xml_cor.setAttribute("T", "" + this.T);
			xml_cor.setAttribute("dT", "" + this.dT);
			xml_cor.setText("" + this.Correlation_Length);
			corLengths.add(xml_cor);

			XMLOutputter outp = new XMLOutputter(Format.getPrettyFormat());
			FileOutputStream outputFile = new FileOutputStream(file_path + "/"
					+ "CorrelationLength.xml");
			outp.output(document, outputFile);
		}
	}

	// here we do simulation and return Magnetization square value
	private Double[] Simlation() {
		// read parameters from configuration file with JDOM
		SAXBuilder builder = new SAXBuilder();
		File xmlFile = new File("src/config/main.xml");
		Document document = null;

		try {
			document = (Document) builder.build(xmlFile);

		} catch (IOException io) {
			System.out.println(io.getMessage());
		} catch (JDOMException jdomex) {
			System.out.println(jdomex.getMessage());
		}

		Element rootNode = document.getRootElement();
		// reading general parameters
		// Element general = rootNode.getChild("general");
		Element model = rootNode.getChild("model");
		String tneighbors = model.getChild(modelName).getChildText("neighbors");
		int[][] neighbors = String2Array.Str2int2(tneighbors);

		ArrayList<Double> tE_J = new ArrayList<Double>();// data for magnetic
															// moment
		// We need find number of layers
		int L = 1;

		if (this.B != 0) {
			L = (int) (1 / (dT * this.T));
		}

		int MCS = 20000;
		int ThermalMCS = 10000;
		int sweepStep = 1;
		Lattice Lat = new Lattice(this.N, this.J, this.B, this.T, L, neighbors,
				this.method);
		// start loop for sweeps
		for (int sweep = 0; sweep <= MCS; sweep++) {
			// update lattice
			Lat.update();
			if (sweep > ThermalMCS) {
				if (sweep % sweepStep == 0) {
					// record data at each appropriate sweep
					// int M = Lat.M();
					tE_J.add(Lat.Energy_J());
				}
			}
		}
		return tE_J.toArray(new Double[0]);
	}

	public int SweepStep() {
		return this.Correlation_Length;
	}

	public int MCS() {
		return 10000 * this.Correlation_Length;
	}

	public int ThermalMCS() {
		return 5000 * this.Correlation_Length;
	}
}
